/**********************************************
*  Classes:   ScriptManager
*  Desc:      Singleton class. Holds scripts and
*		      proxy functions to interface with 
*			  LUA.
*  Author:    Vaios Kalpias-Ilias (C) 2008-2009
***********************************************/

#include "ScriptManager.h"
extern "C"
{
#include <lualib.h>
#include <lauxlib.h>
}
using namespace std;
//----------------------------------------------------
ScriptManager* ScriptManager::m_spInstance = 0;
//----------------------------------------------------
/**
* Ctor
*/
ScriptManager::ScriptManager() : m_pActiveScript(0)
{
}
//----------------------------------------------------
/**
* Dtor
*/
ScriptManager::~ScriptManager()
{
	Shutdown();
}
//----------------------------------------------------
/**
* Gets instance
*/
ScriptManager* ScriptManager::GetInstance()
{
	if (!m_spInstance)
	{
		m_spInstance = new ScriptManager();
	}

	return m_spInstance;

}
//----------------------------------------------------
/**
* Shuts down and destroys instance
*/
void ScriptManager::Destroy()
{
	if (m_spInstance)
	{
		delete m_spInstance;
	}
}
//----------------------------------------------------
/**
* Initializes manager
*/
bool ScriptManager::Init()
{
	return true;
}
//----------------------------------------------------
/**
* Resets manager
*/
bool ScriptManager::Reset()
{
	Shutdown();
	return Init();
}
//----------------------------------------------------
/**
* Loads script
*/
bool ScriptManager::Load(const string& name,
		                 const string& filename)
{
	// create new lua state
	lua_State* state = lua_open();
	if (!state) 
	{
		return false;
	}

	// initialize lua standard library functions
   luaL_openlibs(state);

   // open file
   if (luaL_loadfile(state, filename.c_str()))
	   return false;

   // if the name does not exist
	if (m_Scripts.find(name) == m_Scripts.end())
	{
		// if the script was added
		if(m_Scripts.
			insert(make_pair(name, state)).
			second)
		{
			return true;
		}
	}
	
	// something went wrong
	// so delete memory and return false
	if (state)
	{
		lua_close(state);
	}

	return false;
}
//----------------------------------------------------
/**
* Unloads script
*/
bool ScriptManager::Unload(const string& name)
{
	map<const string, lua_State*>::iterator it;
	// if the name exists
	if ((it = m_Scripts.find(name)) != m_Scripts.end())
	{
		lua_close(it->second);
		m_Scripts.erase(it);
		return true;
	}

	return false;
}
//----------------------------------------------------
/**
* makes a script active to perform operations on it
*/
bool ScriptManager::ActivateScript(const string& name)
{
	map<const string, lua_State*>::iterator it;
	// if the name exists
	if ((it = m_Scripts.find(name)) != m_Scripts.end())
	{
		m_pActiveScript = it->second;
		return true;
	}

	return false;
}
//----------------------------------------------------
/**
* registers c++ function with a script
*/
void ScriptManager::ExportFunction(const char* funcName,
								   lua_CFunction func)
{
	if (m_pActiveScript)
	{
		lua_register(m_pActiveScript, funcName, func);
	}
	
}

//----------------------------------------------------
/**
* returns the size of a table
*/ 
const int ScriptManager::GetTableSize(const char* table)
{
	
	if (m_pActiveScript)
	{
		lua_getglobal(m_pActiveScript, table);
		int value = (int)lua_objlen ( m_pActiveScript, 1);
		lua_pop (m_pActiveScript, 1);
		return value;
	}

	return 0;		
}

//----------------------------------------------------
/**
* returns an int from the lua stack of the active script
*/ 
const int ScriptManager::PopLuaInt(const char* var)
{
	if (m_pActiveScript)
	{
		lua_getglobal(m_pActiveScript, var);
		int value = (int)lua_tonumber( m_pActiveScript, 1);
		lua_pop (m_pActiveScript, 1);
		return value;
	}

	return 0;		
}

//----------------------------------------------------
/**
* returns a float from the lua stack of the active script
*/ 
const float ScriptManager::PopLuaFloat(const char* var)
{
	if (m_pActiveScript)
	{
		lua_getglobal(m_pActiveScript, var);
		float value = (float)lua_tonumber( m_pActiveScript, 1);
		lua_pop (m_pActiveScript, 1);
		return value;
	}

	return 0.0f;		
}
//----------------------------------------------------
/**
* returns a string from the lua stack of the active script
*/ 
const std::string ScriptManager::PopLuaString(const char* var)
{
	if (m_pActiveScript)
	{
		lua_getglobal(m_pActiveScript, var);
		const char* ret = lua_tostring( m_pActiveScript, 1);
		if(ret)
		{
			std::string value(ret);
			lua_pop (m_pActiveScript, 1);
			return value;
		}
	}

	return "";		
}

//----------------------------------------------------
/**
* returns an int from the lua stack of the active script
*/ 
const int ScriptManager::PopLuaIntFromTable(const char* var, const char* table)
{
	if (m_pActiveScript)
	{
		lua_getglobal(m_pActiveScript, table);
		lua_pushstring(m_pActiveScript, var);
		lua_gettable(m_pActiveScript, -2);
		int value = (int)lua_tonumber(m_pActiveScript, -1);
		lua_pop(m_pActiveScript, 2);
		return value;
	}

	return 0;		
}

//----------------------------------------------------
/**
* returns a float from the lua stack of the active script
*/ 
const float ScriptManager::PopLuaFloatFromTable(const char* var, const char* table)
{
	return (float)PopLuaIntFromTable(var, table);		
}
//----------------------------------------------------
/**
* returns a string from the lua stack of the active script
*/ 
const std::string ScriptManager::PopLuaStringFromTable(const char* var, const char* table)
{
	if (m_pActiveScript)
	{
		
		lua_getglobal(m_pActiveScript, table);
		if (!lua_istable(m_pActiveScript, -1)) return "";
		lua_pushstring(m_pActiveScript, var);
		lua_gettable(m_pActiveScript, -2);
		const char* ret = lua_tostring(m_pActiveScript, -1);
		lua_pop(m_pActiveScript, 2);
		if (ret)
		{
			return std::string (ret);

		}
	}

	return "";		
}
//----------------------------------------------------
/**
* Shuts down manager
*/
void ScriptManager::Shutdown()
{
	map<const std::string, lua_State*>::iterator it;
	for (it = m_Scripts.begin(); it != m_Scripts.end(); it++)
	{
		if(it->second)
		{
			lua_close(it->second);
		}
	}

	m_Scripts.clear();
}